var pow_serv = null;
var random_cookie = "";


// PUBLIC API //

/**
 *
 * Adds header lines, or replaces them, in Content-Type's case
 *
 */

function pow_header(line) {
	try {
		if(line.match(/Content-Type: +(.+)/)) {
			var m = line.match(/Content-Type: +(.+)/);
			pow_response.content_type = m[1];
		} else if(line.match(/Location: +(.+)/)) {
			pow_response.status_code = "302 Moved";
			pow_response.extra_headers += line+"\n";
		} else {
			pow_response.extra_headers += line+"\n";
		}
	} catch(e) {
		log_error(e);
		return "";
	}
}

/**
 *
 * Clears page up to this point and starts over.
 *
 */

function pow_clear() {
	pow_response.script_out += "__CLEAR_FLAG__";
	pow_response.clear_flag = true;
}


/**
 *
 * Stops printing to the client. Script continues, anyway.
 * Unreliable, but workaround my have to throw an exception.
 *
 */

function pow_exit() {
	pow_response.script_out += "__EXIT_FLAG__";
	throw("exit_flag");
}

/**
 *
 * Allows the debug interface to operator from the local machine only
 * This exports all SJS calls as client side JS and opens the ajax interface.
 *
 */

function pow_debug() {
	// Should also check login is required
	if(pow_server.REMOTE_HOST == "127.0.0.1") {
		throw("debug_flag");
	}
}

/**
 *
 * Exits the code up to that point and highlights the source code.
 * Only works with localhost for security reasons
 *
 */

function pow_highlight() {
	if(pow_server.REMOTE_HOST == "127.0.0.1") {
		throw("highlight_flag");
	}
}

// END PUBLIC API //


 /**
	* 
	* This is the basis for document, which is invoked for writeln()
	* 
	*/

function pow_Document() {
	this.writeln = function(line) {
		if(!line) {
			return;
		}
		if(typeof(line) != "string") {
			line = line.toString();
		}
		line = line.replace(/\$/mg,"__DOLLAR__");
		pow_response.script_out += line+"\n";
	}
	this.write = function(line) {
		if(!line) {
			return;
		}
		if(typeof(line) != "string") {
			line = line.toString();
		}
		line = line.replace(/\$/mg,"__DOLLAR__");
		pow_response.script_out += line;
	}
}

/**
	* 
	* Turns array to string for pow_info() 
	* 
	*/

function pow_array_tostring(arr) {
	var joined = new Array();
	for (var i in arr) {
	  joined.push(i+": "+arr[i]);
	}
	return joined.join(", ");
}

/**
	* 
	* Returns client side version of server code
	* Uses AJAX interface to communicate 
	* 
	*/

function pow_create_client_code(code) {
  var reg1 = new RegExp("<"+"\\?"+"sjs","gm");
  code = code.replace(reg1, "<script>");
  var reg2 = new RegExp("\\?"+">","gm");
  code = code.replace(reg2, "</script>");
	var env = "";
	env += "var pow_server = new pow_Serv();\n";
	env += "pow_server.REMOTE_HOST = '"+pow_server.REMOTE_HOST+"';\n";
	if(pow_server.POST_FILENAME) {
		env += "pow_server.POST_FILENAME = '"+pow_server.POST_FILENAME+"';\n";
	}
	/* POST */
	for( var i in pow_server.POST) {
		if(i) {
			env += "pow_server.POST['"+i+"'] = '"+pow_server.POST[i]+"';\n";
		}
	}
	/* REQUEST */
	for( var i in pow_server.REQUEST) {
		if(i) { env += "pow_server.REQUEST['"+i+"'] = '"+pow_server.REQUEST[i]+"';\n"; }
	}
	/* GET */
	for( var i in pow_server.GET) {
		if(i) { env += "pow_server.GET['"+i+"'] = '"+pow_server.GET[i]+"';\n"; }
	}
	/* REQUEST_HEADERS */
	for( var i in pow_server.REQUEST_HEADERS) {
		if(i) { env += "pow_server.REQUEST_HEADERS['"+i+"'] = '"+pow_server.REQUEST_HEADERS[i]+"';\n"; }
	}
			var raw_post = pow_server.RAW_POST;
			raw_post = raw_post.replace(/\n/mg,"\\n");
			raw_post = raw_post.replace(/\r/mg,"\\r");
			raw_post = raw_post.replace(/"/mg,"\\\""); //"
			env += "pow_server.RAW_POST = \""+raw_post+"\";\n";
			var raw_request = pow_server.RAW_REQUEST;
			raw_request = raw_request.replace(/\n/mg,"\\n");
			raw_request = raw_request.replace(/\r/mg,"\\r");
			raw_request = raw_request.replace(/"/mg,"\\\""); //"
			env += "pow_server.RAW_REQUEST = \""+raw_request+"\";\n";
			if(pow_server.POST_FILENAME) {
				env += "pow_server.POST_FILENAME = '"+pow_server.POST_FILENAME+"';\n";
			}
	
	code = code.replace(/pow_debug\([^\(\)]*\);/, "var pow_debug_on=true;</script><script src='/system/rpc.js'></script><script>\n"+env);
	return code;
}

/**
	* 
	* Returns highlighted text
	* 
	*/

function pow_highlight_code(code) {
	var head_m = code.match(/^([\r\n]*Content-Location:.*\r\n\r\n)/);
	code = code.replace(/^([\r\n]*Content-Location:.*\r\n\r\n)/,"");
	var lines = code.split(/\n/);
	var bracket_count = 0;
	var indented = "";
	for (var i in lines) {
		var open_count = lines[i].split(/(\{|<\?sjs)/).length;
		var close_count = lines[i].split(/(\}|\?>)/).length;
		if(open_count > 1) { open_count--; }
		if(close_count > 1) { close_count--; }
		bracket_count += open_count - close_count;
		indented += lines[i]+"\n";
		for(var j=0; j < bracket_count; j++) {
			indented += "&nbsp;&nbsp;";
		}
	}
	code = indented.replace(/&nbsp;&nbsp;\s*(\}|\?>)/g,"$1");
	var header = head_m[1];
	code = code.replace(/</g, "&lt;");
	code = code.replace(/>/g, "&gt;");
	//code = code.replace(/<([^\?][^<>\n]*)>/g, "<font color=_SQUOTE_green_SQUOTE_>&lt;$1&gt;</font>");
  var reg1 = new RegExp("<"+"\\?"+"sjs","gm");
  code = code.replace(reg1, "<font color='red'>&lt;?sjs</font>");
  var reg2 = new RegExp("\\?"+">","gm");
  code = code.replace(reg2, "<font color='red'>?&gt;</font><br/>");
  code = code.replace(/"([^"]*)"/g,"_DQUOTE_<font color=_SQUOTE_gray_SQUOTE_>$1</font>_DQUOTE_"); //"
  code = code.replace(/'([^']*)'/g, "_SQUOTE_<font color=_DQUOTE_gray_DQUOTE_>$1</font>_SQUOTE_"); //'

	// POW only
  var hiwords = new Array("header","exit","run_ajax","include","get_request_header","exec","info","server.update_infinilink","highlight","server.log_access","server.get_uploaded_file","server.REWRITE_RULES","file_put_contents","file_delete","download_file","server.crontab_set_state","server.crontab_run","file_list_dir","file_get_log_dir","file_get_data_dir","file_get_docs_dir","file_exists","file_open","file_close","file_line","server","file");
  for(var i in hiwords) {
    var reg = new RegExp("(pow_"+hiwords[i]+")","gm");
    code = code.replace(reg, "<font color='blue'>$1</font>");
  }
	// POW DB
  var hiwords = new Array("Pow_db","Pow_db.exec","Pow_db.drop_database","Pow_db.pretty_print");
  for(var i in hiwords) {
    var reg = new RegExp("(pow_"+hiwords[i]+")","gm");
    code = code.replace(reg, "<font color='blue'>$1</font>");
  }
	// POW DB
  var hiwords = new Array("GET","REMOTE_HOST");
  for(var i in hiwords) {
    var reg = new RegExp("("+hiwords[i]+")","gm");
    code = code.replace(reg, "<font color='blue'>$1</font>");
  }
	// Javascript
  var hiwords = new Array("document.writeln","document.write","Array","Boolean","Date","Error","Function","Math","Number","Object","RegExp","String","this","arguments","break","const","continue","do","while","export","for","import","label","return","switch","throw","try","catch","var","while","with","if","alert");
  for(var i in hiwords) {
    var reg = new RegExp("\\b("+hiwords[i]+")\\b","gm");
    code = code.replace(reg, "<font color='orange'>$1</font>");
  }
  code = code.replace(/_DQUOTE_/gm, '"');
  code = code.replace(/_SQUOTE_/gm, "'");
  code = code.replace(/\n/gm, "<br/>\n");
	code = header + code;
	return code;
}

/*
 *
 * pow_parse_xml(xml) returns a json object after parsing XML
 *
 */

function pow_parse_xml(xml) {
	// create initial json obj
	var json = new Object();
	var xmlparser = Components.classes["@mozilla.org/xmlextras/domparser;1"].
			getService(Components.interfaces.nsIDOMParser);
	var parsed_xml = xmlparser.parseFromString(xml, "text/xml");
  if (parsed_xml.documentElement.tagName == "parsererror") { 
		 return json;
	}
	// CHILDREN
	json = pow_trace_nodes(parsed_xml);
	return json;
}

/*
 *
 * pow_trace_nodes(node) traverses a tree and produces json recursively
 *
 */

function pow_trace_nodes(node) {
	var json = new Object();
	var tag_count = 0;
 	for (var j=0; j<node.childNodes.length; j++) { // xml.rsp
		var nodename = node.childNodes[j].nodeName; // rsp
		if (! json[nodename] ) {
			json[nodename] = new Array();
		} else if(!json[nodename][0]) {
			// SHIFT over
			var tmp = json[nodename];
			json[nodename] = new Array();
			json[nodename][0] = tmp;
		}
		if(node.childNodes[j].nodeType == 1) { // rsp node type 1
			// CHILDREN FIRST
			if(json[nodename][0]) {
				json[nodename].push(pow_trace_nodes(node.childNodes[j]));
			} else {
				json[nodename] = pow_trace_nodes(node.childNodes[j]);
			}
			tag_count = json[nodename].length-1;
			// ATTRIBUTES second
			var atts = node.childNodes[j].attributes; 
			for(var k=0; k < atts.length; k++) { 
				if(tag_count) {
					json[nodename][tag_count][atts.item(k).nodeName] = 
							atts.item(k).nodeValue;
				} else {
					json[nodename][atts.item(k).nodeName] = atts.item(k).nodeValue;
				}
			}
		} else {
			if(node.childNodes[j].nodeValue.match(/[a-zA-Z0-9]/m)) {
				json['TEXT'] = node.childNodes[j].nodeValue;
			}
		}
	}
	return json;
}

/**
	* 
	* Server that can be instantiated. How long until httpd.js catches up? 
	* 
	*/
function pow_Serv(port) {
	this.query_pairs = new Array();
	this.pairs = new Array();
	this.whole_get = "";
	this.root_directory = pow_bc_get_root_directory();
	this.index_directory = pow_bc_get_index_directory();
	this.content_type = "text/html";
	this.response_code = "200 OK";
	// PUBLIC
	this.REQUEST = new Array();
	this.GET =  new Array();
	this.POST =  new Array();
	this.SESSION =  new Array();
	this.SESSION_ID = "";
	this.REWRITE_RULES =  new Array();
	this.REQUEST_HEADERS =  new Array();
	this.RAW_POST = "";
	this.RAW_REQUEST = "";
	this.REMOTE_HOST = "";
	this.POST_FILENAME = "";
	this.SCRIPT_FILENAME = "";
	this.SCRIPT_DIR = "";
	this.REQUEST_URI_PATH = "";
	this.REQUEST_METHOD = "GET";
	// SET ONCE
	this.SERVER_PORT = 6670;
	this.APP_HANDLERS = new Array();
	// END ONCE
	this.error_document_404 = "";
	// END PUBLIC
	this.serv = null;
	this.crontab_on = false;
	this.default_webpage = 
	"\r\n<html>\n<head>\n<title>400 Bad Request</title>\n</head>\n"  +
	"<body>\n<h1>Bad Request</h1>\n" +
	"Invalid URI\n" +
	"</body>\n</html>\n";
	pow_response.script_out = "";
	pow_response.exit_flag = false;
	pow_response.clear_flag = false;
	pow_response.highlight_flag = false;
	pow_response.debug_flag = false;
	pow_response.extra_headers = "";

	// PUBLIC

	/**
    * 
    * Returns uploaded file using POST_FILE var 
    * 
	  */
	this.get_uploaded_file = function() {
		if(!this.POST_FILE) {
			return "";
		} else {
			return this.POST_FILE;
		}
	}
	// END PUBLIC
	this.async_listener = new Async(this);

	/**
    * 
    * Finds <?sjs ?> sections quickly and evals them 
    * 
	  */
	this.process_server_js = function(webpage) {
		try {
			var raw_webpage = webpage;
			webpage = (function(webpage) {
			var document = new pow_Document();
			var m = webpage.match(/Content-Location: (.*)/m);
			var content_location = m[1];
			// escape "?"
			webpage = webpage.replace(/\?/g,"__QUEST__");
			webpage = webpage.replace(/<__QUEST__sjs/g,"<?sjs");
			webpage = webpage.replace(/__QUEST__>/g,"?>");

			var lost_lines = 0;
			if(webpage.indexOf("?") == -1 ) {
				m = false;
			} else {
				m = webpage.match(/([^\?]*)<\?sjs(([^\?]|\n)*)\?>/m);
			}
			var function_offset = 0;
			try {
				throw_error(); // dummy function to find line number
			} catch(e) {
				function_offset = e.lineNumber - 200;
			}

			while(m && pow_response.exit_flag == false) {
				var num_rets = m[1].split(/\n/);
				var offset = num_rets.length + lost_lines + 36;
				var script = m[2];
				script = script.replace(/__QUEST__/g,"?");
				try {
					// pow_response.eval did not scope as intended ...
					// Since document could not print anything in response's scope
					// pow_response.eval(script);
					eval(script);
				} catch (e if e == "exit_flag") {
					pow_response.exit_flag = true;
				} catch (e if e == "highlight_flag") {
					pow_response.exit_flag = true;
					pow_response.highlight_flag = true;
				} catch (e if e == "debug_flag") {
					pow_response.exit_flag = true;
					pow_response.debug_flag = true;
				} catch (e) {
					e.lineNumber -=  function_offset - offset;
					document.write('Error: '+pow_request.req_file+':'+
							e.lineNumber+' '+e.message);
				}

				var before_length = webpage.split(/\n/).length;
				var webpage = webpage.replace( /<\?sjs(([^\?]|\n)*)\?>/m, 
						pow_response.script_out );
				webpage = webpage.replace(/__DOLLAR__/mg,"$");
				lost_lines += before_length - webpage.split(/\n/).length;
				pow_response.script_out = "";
				if(webpage.indexOf("<?sjs") == -1 ) {
					m = false;
				} else {
					m = webpage.match(/([^\?]*)<\?sjs(([^\?]|\n)*)\?>/m);
				}
			}
			webpage = webpage.replace(/__QUEST__/g,"?");
			webpage = webpage.replace(/Content-Length: \d+\r\n/m,"");
			return webpage;
			})(webpage);
			if(pow_response.highlight_flag) {
				webpage = pow_highlight_code(raw_webpage);
			} else if(pow_response.debug_flag) {
				webpage = pow_create_client_code(raw_webpage);
			} else if(pow_response.exit_flag) {
				webpage = webpage.replace(/__EXIT_FLAG__(.|\n|\r)*/m,"");
			}
			if(pow_response.clear_flag) {
				webpage = webpage.replace(/\r\n\r\n(.|\n|\r)*__CLEAR_FLAG__/m,"\r\n\r\n");
			}
			// finally check SESSION and save
			for(var i in pow_server.SESSION) {
				pow_session_end();
				break;
			}
			return webpage;
		} catch(e) {
			log_error(e); return "";
		}
	};

	/**
    * 
    * Applies REWRITE rules, similar to .htaccess rewrite rules 
    * 
	  */

	this.rewrite_request = function(data_in) {
		var request_match = data_in.match(/(GET|POST) *(\/[^ ?]*\??[^ ]*)/);
		if(!request_match) { return data_in; }
		var url = request_match[2];
		if(this.REWRITE_RULES) {
			for(var i in this.REWRITE_RULES) {
				var rule_pair = this.REWRITE_RULES[i];
				if(rule_pair[0] && rule_pair[1]) {
					var re = new RegExp(rule_pair[0]);
					url = url.replace(re, rule_pair[1]);
					data_in = data_in.replace(/(GET|POST) *(\/[^ ?]*\??[^ ]*)/,"$1 "+url);
				}
			}
		}
		return data_in;
	};

	/**
    * 
    * Returns login form 
    * 
	  */

	this.get_login_form = function( last_url ) {
		try {
			var form = "<html><head><title>Breadcrumbs Login</title></head><body>\n";
			form += "<script language='javascript' src='/md5.js'> </script>\n";
			form += "<script>var salt='1234';</script>\n";
			form += "<h2><center>POW Login</center></h2>";
			form += "<form action='"+last_url+"'"+
 	       " name='login' onsubmit=\"e = "+
 	         "forms['login'].elements['password'];"+
 	       "e.value=hex_md5(e.value+salt);\" method='POST'>\n";
			form += "<br/><br/>\n<table>";
			form += "<tr><td>User ID: </td><td> <input id='user' "+
					"type='text' name='userid'  "+
					"size='20' /></td></tr>\n";
			form += "<tr><td>Password: </td><td><input type='password' "+
					"name='password'  "+
					"size='20' /></td></tr>\n";
			form += "<tr><td><input type='submit' value='Login' /></td></tr>\n";
			form += "\n</table>\n";
			form += "</form><br/>\n";
			form += "</body></html>";
			return form;
		} catch(e) {
			log_error(e); return "";
		}
	}

	/**
    * 
    * Returns links for the top of the default pages
    * 
	  */

	this.get_links = function() {
		var links = "<a href='/'>Home</a> <a href='/search.sjs'>Search</a> "+
				"<a href='/help.sjs'>FAQ</a> <a href='http://davidkellogg.com/wiki/Main_Page'>Help</a> ";
		if(this.REMOTE_HOST != "127.0.0.1") {
			links += "<script>var hostname = location.hostname; var cook = document.cookie; cook = cook.replace(/.*bc_id2=/,''); cook=cook.replace(/;.* ?/,''); document.write(\"<a href='http://localhost:"+this.SERVER_PORT + "/addip.sjs?ip=\"+hostname+\"&cookie=\"+cook+\"'>Add This Search</a>\");</script> ";
			links += 
				"<a href='http://addons.mozilla.org/firefox/2954/'>Get Breadcrumbs</a>";
		} else {
			var unique_userid = pow_bc_get_unique_userid();
			links +=
				"<a href='http://davidkellogg.com/infinilink/"+unique_userid+"/'>Infinilink</a>";

		}
		links += "<br/>\n";
		return links;
	}

	/**
    * 
    * Returns validity of password, given salt and userid
    * 
	  */

	this.auto_login_valid = function(pass, salt, userid) {
		try {
			var correct_pass = hex_md5(pow_bc_get_unique_password()+userid);
			var obfusc_pass = hex_md5(correct_pass+salt);
			if(obfusc_pass && obfusc_pass == pass) {
				if(! is_authorized(userid, this.REMOTE_HOST)) {
					return false;
				}
				return true;
			} else {
				return false;
			}
		} catch(e) {
			log_error(e); return "";
		}
	}

	/**
    * 
    * Takes in title and javascript.
		* Returns header for pow page.
    * 
	  */

	this.get_header = function(title, onload_function) {
		try {
			if( !onload_function ) {
				onload_function = 'return true';
			}
			var header = "<html>\n<head>\n<title>"+title;
			header += "</title>\n<link rel='stylesheet' href='pow.css' "+
					"type='text/css'>\n</head>\n"+
					"<meta http-equiv=\"Content-Type\" content=\"text/html; "+
					"charset=utf-8\">\n"+
					"<body onload='"+onload_function+"'>\n";
			header += this.get_links();
			return  header;
		} catch(e) {
			log_error(e); return "";
		}
	}

	/**
    * 
		* Returns standard pow footer
    * 
	  */

	this.get_footer = function() {
		return "\n</body>\n</html>";
	}

	/**
    * 
		* Returns breadcrumbs search form.
    * 
	  */

	this.get_form = function( query ) {
		try {
			if(query) {
				query = query.replace(/\+/g, " ");
			} else {
				query = '';
			}
			var form = "<form method='GET' action='/search.sjs'>\n";
			form += '<input id="search" type="text" name="query" value="'+
					query+'" size=20 />\n';
			form += "<input type='submit' value='Search' />\n";
			form += "</form><br>";
			return form;
		} catch(e) {
			log_error(e); return "";
		}
	}

	this.mime_pref = "";
	this.mimes = new Array();
	this.is_binary = Array();

	this.get_is_binary = function() {
		try {
			if(! this.is_binary) {
				this.is_binary = this.get_mime_types();
			}
			return this.is_binary;
		} catch(e) {
			log_error(e); return "";
		}
	}

	/**
    * 
		* Getter for request's query pairs.
    * 
	  */

	this.get_query_pairs = function() {
		return this.query_pairs;
	}

	/**
    * 
		* Getter for entire requested URI, with query string
    * whole_get is http://localhost/q?query=hello
    * 
	  */

	this.get_whole_get = function() {
		return this.whole_get;
	}

	/**
    * 
    * File to print for client, such as index.html.
    * 
	  */

	this.get_req_file = function() {
		return pow_request.req_file;
	}

	/**
    * 
    * Directory of file requested by client, such as /pub
    * 
	  */

	this.get_req_dir = function() {
		return pow_request.req_dir;
	}

	/**
    * 
    * Returns key value pairs from query string and POST body. 
    * 
	  */

	this.get_pairs = function() {
		return this.pairs;
	}

	/**
    * 
    * Extracts query_pairs from POST body.
    * 
	  */

	this.parse_post_request = function( data_in ) {
		try {
		this.query_pairs = new Array();
		var m = data_in.match(/POST *(\/[^ ?]*)\??([^ ]*)/);
		if(m) {
			// FIRST GET
			this.whole_get = m[1]+"?"+m[2];
			pow_request.req_file = m[1];
			pow_request.req_dir = pow_request.req_file.replace(/\/[^\/]*$/,"");
			m = data_in.match(/\r\n\r\n\s*((.|\n|\r)*)/m);
			if(m) {
				this.RAW_POST = data_in;
				var raw_post = data_in;
				var delimiter = raw_post.match(/(-------.*)/);
				var post_file = raw_post.match(/filename="([^\"]*)"/);
				if(post_file) {
					this.POST_FILENAME = post_file[1];
				}
				var reg = new RegExp("Content-Type:[ a-zA-Z/\\-]*\r\n((.|\r|\n)*)");
				var body = raw_post.match(reg);
				if(body && delimiter) {
					var post_body = body[1];
	  			var regx = new RegExp("--"+delimiter[1],"m");
					var splits = post_body.split(regx);
					this.POST_FILE = splits[0];
					this.POST_FILE = this.POST_FILE.replace(/^\r\n/m,"");
					this.POST_FILE = this.POST_FILE.replace(/\n\r\n$/m,"");
					for(var i=1;i<splits.length-1;i++) {
						var m = splits[i].match(
							/Content-Disposition: form-data; name="([^"]*)"((.|\r|\n)*)/m);//"
						if(m[1] && m[2]) {
							var val = m[2].replace(/^\r\n\r\n/m,"");
							val = val.replace(/\r\n$/m,"");
							this.POST[m[1]] = val;
							this.REQUEST[m[1]] = val;
						}
					}
				} else {
					var post_vars = m[1].split("&");
					this.query_pairs = this.query_pairs.concat(post_vars);
					this.pairs = this.parse_pairs(this.query_pairs);
					this.POST = this.pairs;
					this.REQUEST = this.pairs;
				}
			}
		}
		} catch(e) {
			log_error(e); return "";
		}
	}

	/**
    * 
    * Returns associative array of request headers.
    * 
	  */

	this.get_request_array = function(data_in) {
		var request_headers = new Array();
	 	var raws = data_in.split(/\n/);
 	 	for(var i=0; i < raws.length; i++) {
 	   	var tokens_match = raws[i].match(/^([^:]*): *(.*)/);
 	   	if(tokens_match && tokens_match[1] && tokens_match[2]) {
 	    	request_headers[tokens_match[1]] = tokens_match[2];
 	    	request_headers[tokens_match[1].toLowerCase()] = tokens_match[2];
 	   	}
 	 	}
		return request_headers;
	}

	/**
    * 
    * Parses GET query string and packs them into query_pairs. 
    * 
	  */

	this.parse_get_request = function( data_in ) {
		try {
		this.query_pairs = new Array();
		var m = data_in.match(/(GET|HEAD|POST) *(\/[^ ?]*)\??([^ ]*)/);
		if( m ) {
			var has_query = data_in.match(/(GET|HEAD|POST) *(\/[^ ?]*)\?([^ ]+)/);
			if(has_query) {
				this.whole_get = has_query[2]+"?"+has_query[3];
				pow_request.req_file = has_query[2];
				this.query_pairs = has_query[3].split("&");
			} else {
				this.whole_get = m[2];
				pow_request.req_file = m[2];
			}
			pow_request.req_dir = pow_request.req_file.replace(/\/[^\/]*$/,"");
		}
		this.pairs = this.parse_pairs(this.query_pairs);
		} catch(e) {
			log_error(e); return "";
		}
	};

	/**
    * 
    * Parses any query string and returns associative array of key value pairs.
    * 
	  */

	this.parse_pairs = function( query_pairs ) {
		try {
		var pairs = new Array();
		pow_request.req_file = decodeURIComponent(pow_request.req_file);
		if(query_pairs) {
			for (var i=0; i < query_pairs.length; i++) {
				var query_pair = query_pairs[i].split("=");
				if(query_pair[0] && query_pair[1]) {
					var key = decodeURIComponent(query_pair[0].replace(/\+/g," "));
					var value = decodeURIComponent(query_pair[1].replace(/\+/g," "));
					pairs[ key ] = value;
				}
			} // END FOR i
		}
		return pairs;
		} catch(e) {
			log_error(e); return "";
		}
	};

	/**
    * 
    * Populates environment variables for CGI
    * 
	  */

	this.populate_env = function() {
		try {
	    var env = Components.
 	     classes["@mozilla.org/process/environment;1"].
 	   getService(Components.interfaces.nsIEnvironment);
			// POST RE-CREATION
			var post_string = "";
			for( var i in pow_server.POST) {
				if(post_string) {
					post_string += "&";
				}
				post_string += i+"="+pow_server.POST[i];
			}
			// ENV PACKING
			var dir = pow_file_get_handle("/");
			env.set("QUERY_STRING", pow_server.QUERY_STRING );
			env.set("DOCUMENT_ROOT", dir.path);
			env.set("REMOTE_ADDR", pow_server.REMOTE_HOST);
			env.set("REQUEST_URI", pow_server.REQUEST_URI_PATH);
			env.set("SERVER_PROTOCOL","HTTP/1.1");
			env.set("SERVER_PORT", pow_server.SERVER_PORT);
			env.set("REQUEST_METHOD", pow_server.REQUEST_METHOD);
			env.set("SCRIPT_FILENAME", pow_server.SCRIPT_FILENAME);
			var raw_post = pow_server.RAW_POST;
			raw_posts = raw_post.split(/\r\n\r\n/);
			raw_posts.shift();
			env.set("POST_STRING", raw_posts.join("\r\n\r\n"));
			var special_server_keys = new Array("Content-Length","Content-Type");
			var http_server_keys = new Array("Accept","Accept-Charset",
					"Accept-Encoding","Accept-Language","Cache-Control","Connection",
					"Cookie","Keep-Alive","Referer","User-Agent");
			// LOAD EM UP
			for (var i in special_server_keys) {
				var lower_key = special_server_keys[i];
				var upper_key = special_server_keys[i].toUpperCase();
				upper_key = upper_key.replace(/\-/g,"_");
				if(pow_server.REQUEST_HEADERS[lower_key]) {
					env.set(upper_key, pow_server.REQUEST_HEADERS[lower_key]);
				}
			}
			for (var i in http_server_keys) {
				var lower_key = http_server_keys[i];
				var upper_key = "HTTP_"+http_server_keys[i].toUpperCase();
				upper_key = upper_key.replace(/^ACCEPT/,"HTTP_ACCEPT");
				upper_key = upper_key.replace(/\-/g,"_");
				if(pow_server.REQUEST_HEADERS[lower_key]) {
					env.set(upper_key, pow_server.REQUEST_HEADERS[lower_key]);
				}
			}
			env.set("CONTENT_TYPE",pow_server.CONTENT_TYPE);
		} catch(e) {
			log_error(e); return "";
		}
	}

	/**
    * 
    * Populates mimetypes and binary flags. Most mime-types are taken from
    * mozilla and assumed binary. All others must be designated by the user.
    * 
	  */

	this.get_pow_mime_types = function() {
		try {
			this.mime_pref =  pow_bc_get_breadcrumbs_mime_list();
			if(! this.mime_pref) {
				this.mime_pref =  pow_get_default_mimes();
			}
			var mime_types = this.mime_pref.split(",");
			this.mimes = new Array();
			this.is_binary = new Array();
			var binary = false;
			for( var i=0;i<mime_types.length;i+=3  ) {
				this.mimes[mime_types[i]] = mime_types[i+1];
				if( mime_types[i+2] == "false" ) {

					this.is_binary[mime_types[i]] = false;
				} else {
					this.is_binary[mime_types[i]] = true;
				}
			}
			return this.mimes;
		} catch(e) {
			log_error(e); return "";
		}
	};

	/**
    * 
    * Returns a complete set of user-defined mime-types. 
    * 
	  */

	this.get_mime_types = function() {
		try {
		this.mime_pref = pow_get_mimelist_pref();
		var mime_types = this.mime_pref.split(",");
		this.mimes = new Array();
		this.is_binary = new Array();
		var binary = false;
		for( var i=0;i<mime_types.length;i+=3  ) {
			this.mimes[mime_types[i]] = mime_types[i+1];
			this.is_binary[mime_types[i]] = mime_types[i+2];
		}
		} catch (e) {
			// noop
		}
		return this.mimes;
	}

	/**
    * 
    * Breadcrumbs function to highlight key words on a page.
    * 
	  */

	this.highlight_words = function( webpage, found_words ) {
		try {
			var head_body = webpage.split(/<\/head>/);
			var body = head_body[1];
			var found_words = found_words.split("+");
			for( i in found_words ) {
				var bold_point = RegExp("\\b("+found_words[i]+")\\b", "img");
				var hash_point = RegExp(
					"(__BOLD__)("+found_words[i]+")(__ENDBOLD__)", "im");
				var wrong_bold = RegExp("(<[^<>]*)__(END)?BOLD__([^<>]*>)", "mg");
				body = body.replace( bold_point, "__BOLD__$1__ENDBOLD__" );
				for(var j=0; j < 10 && body.match(wrong_bold); j++) {
					body=body.replace( wrong_bold, "$1$3" );
				}
				body = body.replace( hash_point, 
						"<a href='#"+found_words[i]+
						"' name='"+found_words[i]+"'></a>$1$2$3" );
				body = body.replace( /__BOLD__/mg,
						"<b style='color:black;background-color:#a0ffff'>" );
				body = body.replace( /__ENDBOLD__/mg,"</b>" );
			}
			webpage = head_body[0]+"</head>"+body;
			return webpage;
		} catch(e) {
			log_error(e); return "";
		}
	}

	/**
    * 
    * Grabs current port number and starts listening on that port.
    * 
	  */

	this.start_server = function(port) {
		try {
			pow_global_port = port;
			this.SERVER_PORT = port;
			random_cookie = pow_bc_get_auth_cookie();
			var unique_userid = pow_bc_get_unique_userid();
			if(! random_cookie ) {
				random_cookie = pow_bc_get_random_number();
				pow_bc_set_auth_cookie(random_cookie);
			}
			var loopback_only = pow_bc_get_loopback_only();
			var sock = Components.classes["@mozilla.org/network/server-socket;1"];
			pow_serv = sock.createInstance();
			pow_serv = pow_serv.QueryInterface(Components.interfaces.nsIServerSocket);
			pow_serv.init(port,loopback_only,-1);
			pow_serv.asyncListen(this.async_listener);
			this.serv = pow_serv;
			window.addEventListener('unload', this.stop_server, false);
			if(pow_file_exists("/pow/htdocs/system/startup.sjs")) {
				pow_include("/pow/htdocs/system/startup.sjs");
			}
			return true;
		} catch (e) {
			var s = e.toString();
			if (s.match(/80004005/)) {
				// IN THIS CASE,  a server is already active.
				setTimeout("pow_server.start_server()", 10000);
			} else {
				try {
					log_error(e); return "";
				} catch(ex) {
					// 
				}
			}
			return false;
		}
	}

	/**
    * 
    * Called upon window close or any stop of the server.
    * 
	  */

	this.stop_server = function() {
		// UPON killing a window
		try {
			pow_server.serv.close();
			pow_server_active = false;
		} catch (e) {
			log_error(e); return "";
		}
	}

	/*
   *
   * Logs access to the server with timestamp. Logged in "/pow/logs/access.txt"
   *
	 */

  // PUBLIC
	this.log_access = function(access_msg) {
		var date_string = pow_get_pretty_date();
		access_msg = date_string+access_msg;
		pow_log(access_msg,"/pow/log/access.txt","/pow/log/access.1.txt");
	}

	/*
   *
   * Turns cron engine on or off.
   *
	 */

	this.crontab_set_state = function(cron_on) {
		this.crontab_on = cron_on;
	};

	/*
   *
   * Starts cron engine.
   *
	 */

	this.crontab_run = function(intervals, command) {
		try {
			if(!this.crontab_on) {
				return;
			}
			var m = intervals.match(
					/([\w\*,]+) ([\w\*,]+) ([\w\*,]+) ([\w\*,]+) ([\w\*,]+)/ );
			if(!m) {
				return;
			}
			var date = new Date();
			var current_minute = date.getMinutes();
			var current_hour = date.getHours();
			var current_day_of_month = date.getDate();
			var current_month = date.getMonth();
			var current_day_of_week = date.getDay();
			var minutes = m[1];
			var hours = m[2];
			var days = m[3];
			var months = m[4];
			var days_of_week = m[5];
			var incremental_time = 0;
			if(minutes=="*") {
				minutes = current_minute+1;
			}
			incremental_time = minutes - current_minute;
			if(hours=="*") {
				hours = current_hour;
				if(incremental_time <= 0) {
					hours++;
				}
			}
			incremental_time += 60*(hours - current_hour);
			if(days=="*") {
				days = current_day_of_month;
				if(incremental_time <= 0) {
					days++;
				}
			}
			incremental_time += 60*24*(days - current_day_of_month);
			if(months=="*") {
				months = current_month;
				if(incremental_time <= 0) {
					months++;
				}
			}
			incremental_time += 60*24*28*(months - current_month);
			if(days_of_week=="*") {
				days_of_week = current_day_of_week;
			}
			var target = Number(minutes)+60*Number(hours)+24*60*Number(days);
			var current_time = 
					current_minute+60*current_hour+24*60*current_day_of_month;
			var timeout = 60000*(target-current_time);
			if(timeout > 0 && current_day_of_week == days_of_week) {
				setTimeout(command, timeout);
				command = command.replace(/\'/g,"\\'");
				setTimeout("pow_server.crontab_run('"+intervals+"','"+command+
						"')", timeout);
			}
		} catch (e) {
			log_error(e); return "";
		}
	};

	/*
   *
   * Internal crontab function that runs a command after an interval.
   *
	 */

	this.crontab_set_command = function(intervals, command) {
		try {
			this.crontab_set_state(true);
			this.crontab_run(intervals, command);
		} catch (e) {
			log_error(e); return "";
		}
	};

	/*
   *
   * Remotely updates infinilink with user id and current IP.
   *
	 */

	this.update_infinilink = function() {
		var unique_userid = pow_bc_get_unique_userid();
		var result = pow_download_file("http://davidkellogg.com/pub/infinilink.php?put="+unique_userid);
	}

	// END PUBLIC

	/*
   *
   * Prints XHR-related javascript for Ajax.
   *
	 */

	this.print_xhr = function() {
		var xhr = 
				"<script> \n"+
				"var RESPONSE=0; \n"+
				"var req = false; \n"+
				"function pow_ajax_load(url,action) { \n"+
				"  if(!url.match(/\\?/)) { url += '?AJAX=true&action='+action; }\n" +
				"  try { \n"+
				"    if (window.XMLHttpRequest) { \n"+
        "      req = new XMLHttpRequest();\n"+
				"    } else if (window.ActiveXObject) { \n"+
   			"      req = new ActiveXObject('Microsoft.XMLHTTP');\n"+
				"    }\n"+
				"  } catch(e) { req = false; } \n"+
				" "+
				"  if(req) { \n"+ 
				"    if(action =='append') { \n"+
				"      req.onreadystatechange = handle_xhr_append; \n"+
				"    } else if(action == 'replace') { \n"+
				"      req.onreadystatechange = handle_xhr_replace; \n"+
				"    } else {\n"+
				"      req.onreadystatechange = handle_xhr_replace; \n"+
				"    }\n"+
				"     \n"+
				"    req.open(\"GET\", url, true); \n"+
				"    req.send(\"\"); \n"+
				"  } \n"+
				"  return false;  \n" +
				"} \n"+
				"function handle_xhr_replace() { \n"+
				"  if (req.readyState == 4) { \n"+
				"    if (req.status == 200) { \n"+
				"      eval(req.responseText); \n"+
				"      var el = document.getElementById(RESPONSE.element); \n"+
				"      el.innerHTML = RESPONSE.val; \n"+
				"    } else { \n"+
				"      return \"ERROR: \"+req.statusText; \n"+
				"    } \n"+
				"  } \n"+
				"} \n"+
				"function handle_xhr_append() { \n"+
				"  if (req.readyState == 4) { \n"+
				"    if (req.status == 200) { \n"+
				"      eval(req.responseText); \n"+
				"      var el = document.getElementById(RESPONSE.element); \n"+
				"      var oNewNode = document.createElement('DIV'); \n"+
				"	     el.appendChild(oNewNode); \n"+
				"		   oNewNode.innerHTML=RESPONSE.val;\n"+
				"    } else { \n"+
				"      return \"ERROR: \"+req.statusText; \n"+
				"    } \n"+
				"  } \n"+
				"} \n"+
				"</script>		\n";
		return xhr;
	};

	/*
   *
   * Starts data pump for asynchronous input of data from client.
   *
	 */

	this.prime_pump = function(listener, stream) {
 	  this.pow_pump = Components.
 	  	classes["@mozilla.org/network/input-stream-pump;1"].
 	  createInstance(Components.interfaces.nsIInputStreamPump);
 		this.pow_pump.init(stream, -1, -1, 1000000, 100, true); // 8 sec/100 google.gif
 	 	this.pow_pump.asyncRead(listener, null);
	};
} // END pow_Server

	/*
   *
   * Asynchronous listening object.
   * This shows what to do when data are received.
   *
	 */

function Async(server) {

	 /*
    *
    * Called when the server is stopped in any way, such as a window closing.
    *
	  */

	this.onStopListening = function(serv, status) {
		// noop
	};

	 /*
    *
		* Called after connection established
    *
	  */
	this.onSocketAccepted = function(serv, transport) {
		try {
			// CLEAR OLD DATA
			pow_request = new Pow_Request();
			pow_response = new Pow_Response();
			// END CLEAR

			// INIT STREAMS
			var outstream = transport.openOutputStream(0,10000000,100000);
   	 	var stream = transport.openInputStream(0,0,0);
   	 	var instream=Components.classes["@mozilla.org/scriptableinputstream;1"]
   	   .createInstance(Components.interfaces.nsIScriptableInputStream);
   	 	instream.init(stream);
			// END STREAMS

			// CONFIGURE TRANSPORT
			server.REMOTE_HOST=transport.host;
			if(transport.setTimeout) { // FF 1.0.0 does not allow this
				transport.setTimeout(1, 5); // 5s timeout
			}
			// END CONFIGURE TRANSPORT

	 		// READ DATA
	 		this.dataListener = new DataList( server, instream, outstream, stream );
			// PLACE DOS byte-by-byte data stop here
			server.prime_pump(this.dataListener, stream);
			// END READ DATA
		} catch (e) {
			log_error(e); return "";
		} // END TRY
	} // END Socket accepted
} // END Async

 /*
  *
  * Data Listener object that shows what to do with data once received.
  *
  */

function DataList( server, instream, outstream, stream ) {
   this.found_return = 0;
	 pow_request.req_file = "";
	 pow_response.cookie_found = "";
	 pow_response.cookie = "";
	 this.whole_get = "";
	 this.data_in = "";
	 this.ssl_count = 0;

  /*
   *
   * Called when listening startedd, before data is received. Noop.
   *
   */

   this.onStartRequest = function(request, context) { };

  /*
   *
   * Called when server stopped. Ties loose ends.
   *
   */

   this.onStopRequest = function(request, context, status) {
     instream.close();
     outstream.close();
   };
	 // called when there are new data
	 // SSL vars
   this.transport2 = "";
		this.bos2 = "";
		this.bis2 = "";
		this.outstream2 = "";
		this.intstream2 = "";
		this.stream2 = "";
		// END SSL
 /*
  *
  * Called when data comes in.
  *
  */

	this.onDataAvailable = function( request, 
																context, 
																inputStream, 
																offset, 
																count ) {
		try {
			// SET UP VARS
			var query_pairs = new Array();
			var content_type = "text/html";
			var status_code = "200 OK";
			var extra_headers = "";
			var webpage = server.default_webpage;
			var content_location = "";
			var request_valid = true;
			var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
     			.createInstance( Components.interfaces.nsIBinaryInputStream );
			pow_request.req_file = "";
			bis.setInputStream(stream);
			var data_chunk = bis.readBytes(count);
 	    this.data_in += data_chunk;
			if(! this.data_in.match(/\r\n\r\n/)) {
				this.ssl_count++;
			}
			server.GET = new Array();
			server.REQUEST = new Array();
			server.POST = new Array();
			server.SESSION = new Array(); 
			server.SESSION_ID = "";
			server.RAW_POST = "";
			server.SCRIPT_FILENAME = "";
			server.REQUEST_URI_PATH = "";
			server.SCRIPT_DIR = "";
			server.CONTENT_TYPE = "";
			server.RAW_REQUEST = this.data_in;
			server.REQUEST_HEADERS = 
					server.get_request_array(this.data_in);
			var head_request = false;
			// END VARS

			// LOAD DATA IN
			if( this.data_in.match("([\n]{2}|[\r]{2}|(\r\n){2})") ) {
				this.found_return++;
			}
			var request_lines = this.data_in.split(/\n/);
			this.data_in = server.rewrite_request(this.data_in);
			// END DATA

			// LOAD QUERY PAIRS (/X... is for websockets!)
			var get_match = this.data_in.match(/GET *(\/X[^ ]*)\??([^ ]*)/);
			
			var aryTmp = new Array();
			for( var i in request_lines){
				if(request_lines[i].match(/Host: *([^:]+)/)){
					aryTmp = request_lines[i].match(/Host: *([^:]+)/);
					server.REQUEST_HOST = aryTmp[1];
				}
			}
			
			if(get_match) {
				server.REQUEST_METHOD="GETWS";

				if(pow_wsserver == null){
					pow_wsserver = new pow_wsServ(this);
					var success = pow_wsserver.start_server(10081);
				}

				server.QUERY_STRING = "";
				server.parse_get_request(this.data_in);
				var pairs = server.get_pairs();
				server.GET = pairs;
				request_valid = true;

			} else if(this.data_in.match(/GET *(\/[^ ?]*)\??([^ ]*)/)) {
				// this code is stupid..
				get_match = this.data_in.match(/GET *(\/[^ ?]*)\??([^ ]*)/);
				
				server.REQUEST_METHOD="GET";
				server.QUERY_STRING=get_match[2];
				server.parse_get_request(this.data_in);
				var pairs = server.get_pairs();
				server.GET = pairs;
				request_valid = true;
			} else if(this.data_in.match(/POST *(\/[^ ?]*)\??([^ ]*)/)) {
				server.REQUEST_METHOD="POST";
				var m = this.data_in.match(/Content-Length: *(\d+)/);
				if(m) {
					var expected_length = m[1];
					// Continue if not all data received
					if(expected_length > this.data_in.length) {
						return;
					}
				}
				server.parse_get_request(this.data_in);
				var pairs = server.get_pairs();
				server.GET = pairs;
				server.parse_post_request(this.data_in);
				// FILE post content-type
				var m2 = this.data_in.match(/Content-Type: *([^\r\n]+)/i);
				if(m2) {
					server.CONTENT_TYPE=m2[1];
				}
				request_valid = true;
			} else if(this.data_in.match(/HEAD *(\/[^ ?]*)\??([^ ]*)/)) {
				server.parse_get_request(this.data_in);
				var pairs = server.get_pairs();
				server.GET = pairs;
				request_valid = true;
				head_request = true;
			} else {
				var pairs = server.get_pairs();
				server.GET = pairs;
				status_code = "400 Bad Request";
				request_valid = false;
			}
			for(var i in server.GET) {
				server.REQUEST[i] = server.GET[i];
			}
			for(var i in server.POST) {
				server.REQUEST[i] = server.POST[i];
			}
			// END PAIRS

			// WHAT DOES IT WANT?
			var whole_get = server.get_whole_get();
			var filename = server.get_req_file();
			pow_request.req_file = server.root_directory + filename;
			pow_request.req_dir = server.get_req_dir();
			server.REQUEST_URI_PATH=filename;
			server.SCRIPT_FILENAME=pow_request.req_file;
			server.SCRIPT_DIR=pow_request.req_file.replace(/\/[^\/]*$/,"");
			// END WANT

			// REWRITE REQUEST
			pow_request.req_file = pow_request.req_file.
					replace(/\/htdocs\/pages\//,"/pages/");
			if(pow_request.req_file.match(/%/)) {
				pow_request.req_file = decodeURIComponent(pow_request.req_file);
			}
			pow_request.req_file = pow_request.req_file.replace(/\.{2,}/g,"");
			// END REWRITE

			// HANDLE COOKIES
			var cookie_test = new RegExp("Cookie:.*");
			var cookie_enabled = this.data_in.match(cookie_test);
			var cookie_match = new RegExp("Cookie:.*bc_id2=("+random_cookie+".*)");
			var c2 = this.data_in.match(cookie_match);
			if(!c2) {
				// cookieless cookie
				if(server.GET['cookie'] == random_cookie) {
					c2 = true;
				}
			}
			var js1 = pow_request.req_file.match(/\/htdocs\/md5.js$/);
			if(c2 || js1) {
				pow_response.cookie_found = true;
			} else if(request_valid) {
				webpage = "\r\n"+server.get_login_form(whole_get);
			}
			// END COOKIES

			// PASSWORD CHECK
			var pass = pow_bc_get_password();
			var login = pow_bc_get_login();
			var now = new Date();
			var expires = new Date();
			var salt = "1234";
			expires.setTime(now.getTime()+1000000*3600*24);
			var expires_str = expires.toGMTString();
			if( cookie_enabled && server.POST['userid'] &&
					( server.auto_login_valid( server.POST['password'], salt, 
						server.POST['userid'])  || 
						( server.POST['userid'] == login && 
							( server.POST['password'] == pass || 
								server.POST['password'] == hex_md5(pass+salt)
							) 
						)
					)
				) {
				pow_response.cookie = "Set-Cookie: bc_id2="+random_cookie+"; expires="+
						expires_str+"; path=/;\r\n";
				pow_response.cookie_found = true;
			} else if( ! cookie_enabled && pairs['userid'] ) {
				webpage += "<font color='red'>Please enable cookies to "+
						"view this page.</font><br>";
				pow_response.cookie = "Set-Cookie: bc_test=true; expires="+
						expires_str+"; path=/;\r\n";
			} else if( ! cookie_enabled  ) {
				// Unnecesary
				pow_response.cookie = "Set-Cookie: bc_test=true; expires="+
						expires_str+"; path=/;\r\n";
			}
			if( ! pow_bc_pass_protected() || pow_response.cookie_found ) {
				pow_response.cookie_found = true;
			}
			// END PASSWORD CHECK

			// CHECK FOR RETURN
			if( ! this.found_return && ! this.ssl_count) {
				return;
			}
			// END RETURN

			// CHECK MIME TYPE
			var mimes = server.get_pow_mime_types();
			var is_binary = server.get_is_binary();
			this.ext = pow_request.req_file.match(/\.([^\.\/]*)$/)
			this.mime = "text/html";
			var binary = false;
			if(this.ext) {
				this.ext[1] = this.ext[1].toLowerCase();
				this.mime = mimes[this.ext[1]];
				binary = is_binary[this.ext[1]];
				content_type = this.mime;
			} else {
				this.mime = "text/html";
				binary = false;
				content_type = this.mime;
			}
			// END MIME

			// GET CONTENT
			var extension = "";
			if(!this.mime && this.ext[1]) {
				try {
					var mimeh = Components.classes["@mozilla.org/mime;1"]
     				.getService(Components.interfaces.nsIMIMEService);
		 			this.mime = mimeh.getTypeFromExtension(this.ext[1]);
					binary = pow_get_binary_from_extension(this.ext[1]);
					extension = this.ext[1].toLowerCase();
				} catch (e) {
					this.mime = "text/html";
				}
			}
			if( this.mime && pow_response.cookie_found && request_valid ) {
				content_type = this.mime;
				if(binary && ( extension && !pow_server.APP_HANDLERS[extension])) {
					webpage = "\r\n"+pow_get_file(pow_request.req_file,"rb");
				} else if(binary) {
					webpage = "\r\n"+pow_get_file(pow_request.req_file,"rb");
				} else {
					webpage = "\r\n"+pow_get_file(pow_request.req_file,"r");
					var m = webpage.match(/Content-Location: (.*)/m);
					if(m) {
						var ext_match =  m[1].match(/\.(\w+)$/);
						if(ext_match) {
							extension = ext_match[1].toLowerCase();
						}
						content_location = m[1];
						// A TRAILING slash problem
						if(! pow_request.req_file.match(content_location) && 
								(pow_request.req_file+"/").match(content_location) ) {
								status_code = "301 Moved Permanently";
								extra_headers += "Location: "+content_location+"\r\n";
								webpage = "\r\n<center><h2>301 Moved Permanently</h2>"+
										"</center> "+content_location;
						}
					}
					if(m && m[1].match(/\.sjs/i) &&  
							pow_request.req_file.match(/^\/[^\/]+\/htdocs/) ) {
						req_string = this.data_in;
						content_type = "text/html";
						pow_response.script_out = "";
						webpage = server.process_server_js(webpage);
						content_type = pow_response.content_type;
						extra_headers = pow_response.extra_headers;
						status_code = pow_response.status_code;
					} else if(pow_server.APP_HANDLERS[extension]) {
						var leaf_match = pow_request.req_file.match(/\/([^\/]+)$/);
						var leaf = "";
						if(leaf_match && leaf_match[1]) {
							leaf = leaf_match[1];
						}
						if(leaf) {
							pow_server.populate_env();
				    	var dir = pow_file_get_handle("/");
				    	//var args = new Array(pow_server.APP_HANDLERS[extension], leaf,
     				 // 	 dir.path+pow_server.SCRIPT_DIR, dir.path);
							var leaf_a = new Array(leaf);
				    	var args = new Array(pow_server.APP_HANDLERS[extension],
     				  	 dir.path+pow_server.SCRIPT_DIR, dir.path, leaf_a);
							if(navigator.oscpu.match(/Windows/)) {
								webpage = "\r\n"+pow_pipe_exec("/pow/bin/makepipe.bat", args, true);
							} else {
								//alert(args);
								webpage = "\r\n"+pow_pipe_exec("/pow/bin/makepipe.sh", args, true);
							}
							var type_regex = new RegExp("Content-Type: ([^\\r\\n]*)", "mi");
							var type_match = webpage.match(type_regex);
							if(type_match) {
								content_type = type_match[1];
								var header_match = webpage.split(/(\r\n\r\n|\n\n)/);
								extra_headers = header_match.shift();
								extra_headers = extra_headers.replace(type_regex,"");
								extra_headers = extra_headers.replace(/^\r\n/m,"");
								extra_headers = extra_headers.replace(/(\r\n\r\n|\n\n)/m,"\r\n");
								header_match.shift(); // rid separator
								webpage = "\r\n"+header_match.join("");
							}
						}
					}
				}
			} else if( pow_response.cookie_found && request_valid ) {
				content_type = "text/html";
				webpage = pow_get_file(pow_request.req_file,"r");
				//alert("webpage is "+webpage);
			}
			webpage = webpage.replace(/Content-Location: (.*)\r\n\r\n/m, "");
			// END CONTENT

			// INTERNAL LENGTH
			if(pow_response.cookie_found && pairs['found']) {
				webpage = server.highlight_words(webpage, pairs['found']);
				// INTERNAL USE ONLY
				webpage = webpage.replace(/Content-Length/,"X-Content-Length");
			}
			// END LENGTH

			// CHECK BAD PAGES
			if(  webpage == undefined ) {
				// THIS is really a 505 error or the like
				webpage = "ERROR";
			}
			if(webpage.match(/^[\r\n]*ERROR/)) {
				status_code = "404 Not Found";
				if(pow_server.error_document_404) {
					webpage = "Content-Location: /404.sjs\r\n\r\n"+
							pow_file(pow_server.error_document_404);
					if(pow_server.error_document_404.match(/\.sjs/i) &&  
							pow_request.req_file.match(/^\/[^\/]+\/htdocs/) ) {
						req_string = this.data_in;
						content_type = "text/html";
						pow_response.script_out = "";
						webpage = server.process_server_js(webpage);
						content_type = pow_response.content_type;
						extra_headers = pow_response.extra_headers;
					}
				} else {
					webpage = "\r\n<html>\n<head>\n<br>\n<center><h2>404 Not Found"+
							"</h2></center>\n<br>";
				}
				if(content_location) {
					webpage += content_location+ " was not found.";
				}
			}
			// END BAD

			// FINALIZE HEADERS AND CLOSE
			extra_headers += "Server: POW/"+pow_version+"\r\n"; 
			extra_headers += "Content-Length: "+(webpage.length-2)+"\r\n";
			// var page_length = webpage.length;
			webpage = "HTTP/1.1 "+status_code+"\r\n"+pow_response.cookie+
					"Content-Type: " + 
			  	content_type +"\r\n"+extra_headers +webpage;
			server.log_access(server.REMOTE_HOST + " "+server.REQUEST_METHOD+
						" "+status_code+" "+pow_request.req_file);
	// SSL TRY // IS THIS NECESSARY?
			/*if(! webpage.match(/Content-Length/m)) {
				webpage = webpage.replace(/(\r\n\r\n)/m,"\r\nContent-Length: "+webpage.length+"$1");
			}*/
			if (this.ssl_count) {
			// noop
			} else { // END ssl_count
				if( ! binary ) {
					var converter = 
						Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
				  	.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
					converter.charset = "UTF-8";
					webpage = converter.ConvertFromUnicode(webpage);
				}
				//alert(webpage);
 				outstream.write(webpage, webpage.length);
				instream.close();
				outstream.close();
			} // END ssl_count
		} catch (e) {
			log_error(e); return "";
		} // END TRY
	} // END ON DATA AVAILABLE

} // END DataList
